#ifndef UDP_SERVER_HPP__
#define UDP_SERVER_HPP__

#include <iostream>
#include <string.h>
#include <strings.h>
#include <error.h>
#include <functional>

#include "inetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"
#include "ThreadPool.hpp"

// 封装版本不需要
// std::string gdefaultip = "127.0.0.1"; // 本地环回IP //本地通信或代码测试就用这个IP
uint16_t gdefaultport = 8080; // 尽量选用没人用的端口号
int gdefaultsockfd = -1;
using namespace LogModule;
using namespace ThreadPoolModule;

using adduser_t = std::function<void(InetAddr &id)>;
using remove_t = std::function<void(InetAddr &id)>;
using route_t = std::function<void(int sockfd, std::string &message)>;
using task_t = std::function<void()>;
class nocopy
{
public:
    nocopy() {}
    nocopy(const nocopy &) = delete;
    const nocopy &operator=(const nocopy &) = delete;
    ~nocopy() {}
};
class UdpServer : public nocopy
{
public:
    UdpServer(uint16_t port = gdefaultport)
        : _sockfd(gdefaultsockfd), _addr(port), _isrunning(false)
    {
    }
    void Init()
    {

        // 1.创建socket套接字
        // 第一个参数网络通信,第二个参数是数据报,Udp协议,第三个参数传0即可
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(Loglevel::FATAL) << "socket" << strerror(errno);
            Die((int)Code::SOCKETERROR);
        }
        LOG(Loglevel::INFO) << "socket success";
        /*
        // 2.填充网络信息并bind绑定
        // 这里只是填充网络信息,bind才是设置进内核
        struct sockaddr_in local;
        bzero(&local, sizeof(local));                       // 将local全置为0,确定性
        local.sin_family = AF_INET;                         // 必须和套接字的一样
        local.sin_port = htons(_port);                      // 设置进内核的port和ip是要发送到网络中的,port要从本地序列转化为网络序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str());     // ip要从string转化为4bytes,还要是网络序列
        local.sin_addr.s_addr=INADDR_ANY;
        */
        // int n = bind(_sockfd, CONV(&local), sizeof(local)); // 一定要注意这里的类型转化
        int n = bind(_sockfd, _addr.NetAddr(), _addr.NetAddrLen());
        if (n < 0)
        {
            LOG(Loglevel::FATAL) << "bind" << strerror(errno);
            Die((int)Code::BINDERROR);
        }
        LOG(Loglevel::INFO) << "bind success";
    }
    // 注册服务,以后要加什么业务在这注册即可
    void RegisterService(adduser_t adduser, route_t route, remove_t remove)
    {
        _adduser = adduser;
        _route = route;
        _remove = remove;
    }

    void Start()
    {
        _isrunning = true;
        while (true) // 服务器运行起来就不会停止
        {
            // 收到客户端发的信息,将客户端的信息发回去
            char inbuffer[1024];
            struct sockaddr_in peer;      // 必须要有,用来保存谁发来的信息
            socklen_t len = sizeof(peer); //-1预留一个空位给\0
            int n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, CONV(&peer), &len);
            if (n > 0)
            {
                // // 发消息方的ip地址和端口号
                InetAddr cli(peer);
                inbuffer[n] = 0;
                // std::string message = inbuffer;
                std::string message;
                if (strcmp(inbuffer, "QUIT") == 0) // 用户退出
                {
                    _remove(cli);
                    message = cli.Addr() + "# 我走了,你们聊";
                }
                else
                {
                    _adduser(cli);
                    message = cli.Addr() + "#" + inbuffer;
                }
                // _adduser(cli); // 收到消息后新增用户(已经做了重复的处理了)
                // // 构建转发内容,交给线程池,让线程池进行转发

                task_t task = std::bind(UdpServer::_route, _sockfd, message); // route 是回调方法，不是成员方法 不用 this
                // bind 1.绑定参数 将参数 _sockfd 和 message 预先绑定到成员函数 UdpServer::_route 上。
                //      2.延迟调用 生成一个新的可调用对象 task，后续可以通过 task() 直接调用，而无需再次传递 _sockfd 和 message。
                ThreadPool<task_t>::getInstance()->Equeue(task);

                // LOG(Loglevel::DEBUG) << "Client" << cli.Ip() << ":" << std::to_string(cli.Port()) << "says#" << inbuffer;
                // std::string echo_string = "echo#";
                // echo_string += inbuffer;
                // sendto(_sockfd, echo_string.c_str(), sizeof(echo_string), 0, CONV(&peer), len);
            }
        }
        _isrunning = false;
    }
    ~UdpServer()
    {
        if (_sockfd > gdefaultsockfd)
            ::close(_sockfd);
    }

private:
    int _sockfd;
    InetAddr _addr;
    // std::string _ip;
    uint16_t _port;
    bool _isrunning;

    // 新增用户业务,网络模块负责新增用户和路由消息
    adduser_t _adduser;
    route_t _route;
    remove_t _remove;
};

#endif